﻿using LightCAD.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LightCAD.Runtime
{

    public class SnapRefCurve
    {
        public SnapPointType Type { get; set; }
        public ICurve2d Curve { get; set; }

        public SnapRefCurve(SnapPointType type, ICurve2d curve)
        {
            Type = type;
            Curve = curve;
        }   
    }
    
    public class SnapPointResult
    {
        public LcElement Element { get; set; }

        public Vector2d? Point { get; set; }
        /// <summary>
        /// 相关联的元素
        /// </summary>
        public List<SnapRefCurve> Curves { get; } = new List<SnapRefCurve>();

        public string Name { get; set; }
        public bool IsTemp { get; set; } = true;
    }

    public class SnapResult
    {
        public Vector2d MousePoint { get; set; }
        public Vector2d? SnapPoint { get; set; }
        public List<SnapRefCurve> Curves { get; } = new List<SnapRefCurve>();


        internal void SetSnap(SnapPointResult result)
        {
            this.SnapPoint = result.Point;
            this.Curves.AddRange(result.Curves);
        }

        internal void AddSnap(SnapPointResult result)
        {
            this.Curves.AddRange(result.Curves);
        }
    }
    public class SnapRuntime
    {
        private ViewportRuntime vprotRt;
        private Task houverTask;
        private CancellationTokenSource houverTaskCTS;
        private Task moveTask;
        private CancellationTokenSource moveTaskCTS;

        public SnapResult Current { get; set; }
        /// <summary>
        /// 捕捉参考点
        /// </summary>
        public List<SnapPointResult> RefPoints { get; } = new List<SnapPointResult>();
        /// <summary>
        /// 捕捉参考线，靠近悬停后选中，再悬停取消
        /// </summary>
        public List<ICurve2d> RefCurves { get; } = new List<ICurve2d>();
        public SnapRuntime(ViewportRuntime vportRt)
        {
            this.vprotRt = vportRt;
        }

        public void MouseHover(Vector2d mousePoint)
        {
            if (this.vprotRt == null)
            {
                return;
            }
            if (houverTask != null)
            {
                houverTaskCTS.Cancel();
                houverTask = null;
                houverTaskCTS = null;
            }

            houverTaskCTS = new CancellationTokenSource();
            houverTask = new Task(DoHoverTask, mousePoint, houverTaskCTS.Token);
            houverTask.Start();
        }
        public void MouseMove(Vector2d mousePoint)
        {
            if (this.vprotRt == null)
            {
                return;
            }

            if (moveTask != null)
            {
                moveTaskCTS.Cancel();
                moveTask = null;
                moveTaskCTS = null;
            }

            moveTaskCTS = new CancellationTokenSource();
            moveTask = new Task(DoMoveTask, mousePoint, moveTaskCTS.Token);
            moveTask.Start();
        }

        private void DoHoverTask(object state)
        {
            var mousePoint = (Vector2d)state;
            var elements = this.vprotRt.GetElementsInBounds(houverTaskCTS);
            var news = new List<SnapPointResult>();
            foreach (var element in elements)
            {
                if (houverTaskCTS.IsCancellationRequested) break;

                var eleAction = (element.RtAction as ElementAction);
                var snapEleResult = eleAction.SetViewport(this.vprotRt).SnapPoint(this,element, mousePoint, true);
                if (snapEleResult != null)
                {
                    var refPoint= RefPoints.FirstOrDefault((item) => item.Point == snapEleResult.Point) ;
                    if (refPoint!=null )
                    {
                        if (!refPoint.IsTemp)//不是临时参考点，再次Hover就移除
                        {
                            RefPoints.Remove(refPoint);
                        }
                        else
                        {
                            refPoint.Curves.AddRange(snapEleResult.Curves);
                        }
                    }
                    else
                    {
                        refPoint = new SnapPointResult();
                        refPoint.Point = snapEleResult.Point;
                        refPoint.Curves.AddRange(snapEleResult.Curves);
                        RefPoints.Add(refPoint);
                        news.Add(refPoint);
                    }
                }
            }
            //foreach(var rp in news)
            //{
            //    rp.IsTemp = false;
            //}
            houverTask = null;
            houverTaskCTS = null;
        }
       
        private void DoMoveTask(object state)
        {
            var mousePoint = (Vector2d)state;
            var elements = this.vprotRt.GetElementsInBounds(moveTaskCTS);
            var result = new SnapResult();
            result.MousePoint = mousePoint;
            foreach (var element in elements)
            {
                if (moveTaskCTS.IsCancellationRequested) break;

                var eleAction = (element.RtAction as ElementAction);
                var snapEleResult= eleAction.SetViewport(this.vprotRt).SnapPoint(this,element, mousePoint, false);

                if (snapEleResult != null )
                {
                    if(result.SnapPoint==null )
                    {
                        //如果还未捕捉点，将元素结果加入集合
                        result.SetSnap(snapEleResult);
                    }
                    else if( snapEleResult.Point == result.SnapPoint)
                    {
                        result.AddSnap(snapEleResult);
                    }
                    else
                    {
                        //先捕捉的先作为结果，后捕捉的忽略
                    }
                }
            }

            if (result.SnapPoint != null)
                this.Current = result;
            else
                this.Current = null;

            moveTask = null;
            moveTaskCTS = null;
        }

        public List<SnapPointResult> GetRefPoints(LcElement element)
        {
            var result = new List<SnapPointResult>();
            foreach(var rp in this.RefPoints)
            {
                if (element == rp.Element)
                {
                    result.Add(rp);
                }
                else
                {
                    foreach(var refCurve in rp.Curves)
                    {
                        if(refCurve.Curve.Source == element)
                        {
                            result.Add(rp);
                            break;
                        }
                    }
                }
            }
            return result;
        }
    }
}
